<?php


namespace app\api\service;


use app\api\model\Product;
use app\api\model\UserAddress;
use app\exception\OrderException;
use app\exception\UserException;
use http\Exception;
use think\Db;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use app\api\model\Order as OrderModel;
use app\api\model\OrderProduct as OrderProductModel;

class Order
{
    // 订单的商品列表 也就是客户端传递过来的products参数
    protected $oProducts;
    // 真实的商品信息 （包括库存量）
    protected $products;
    protected $uid;

    /**
     * @param $uid
     * @param $oProducts
     * 作用 检测库存量  通过 创建订单
     *
     *
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws OrderException|UserException|Exception
     */
    public function place($uid,$oProducts): array
    {
        // oProducts 和 products 作对比
        // 从products 才能数据库中查询出来
        $this->oProducts = $oProducts; //二维数组
        $this->products = $this->getProductsByOrder($oProducts);
        $this->uid = $uid;
        $status = $this->getOrderStatus();
        if(!$status['pass']){
            $status['order_id'] = -1;
            return $status;
        }

        // 创建订单快照
        $orderSnap = $this->snapOrder($status);
        // 创建订单
        $order = $this->createOrder($orderSnap);
        $order['pass'] = true;
        return $order;
    }

    /**
     * @param $snap
     *
     * @return array
     *
     * @throws Exception
     */
    private function createOrder($snap): array
    {
        //开始事务
        \think\facade\Db::startTrans();
        try{
            $orderNo = self::makeOrderNo();
            $order = new OrderModel();
            $order->user_id = $this->uid;
            $order->order_no = $orderNo;
            $order->total_price = $snap['orderPrice'];
            $order->total_count = $snap['totalCount'];
            $order->snap_img = $snap['snapImg'];
            $order->snap_name = $snap['snapName'];
            $order->snap_address = $snap['snapAddress'];
            $order->snap_items = json_encode($snap['pStatus']);
            $order->save();
            $orderId = $order->id;
            $create_time = $order->create_time;
            foreach ($this->oProducts as &$p){
                $p['order_id'] = $orderId;
            }
            $orderProduct = new OrderProductModel();
            $orderProduct->saveAll($this->oProducts);
            // 提交事务
            \think\facade\Db::commit();
            return [
                'order_no'=>$orderNo,
                'order_id'=>$orderId,
                'create_time'=>$create_time
            ];
        }catch (Exception $e){
            // 回滚事务
            \think\facade\Db::rollback();
            throw $e;
        }
    }

    /**
     * 生成唯一订单号
     */
    public static function makeOrderNo(): string
    {
        $yCode = array('A','B','C','D','E','F','G','H','I','J');
        return $yCode[intval(date('Y')) - 2021].strtoupper(dechex(date('m')))
               .date('d').substr(time(),-5).substr(microtime(),2,5)
               .sprintf("%02d",rand(0,99));
    }
    /**
     * 生成订单快照
     *
     * @throws UserException
     */
    private function snapOrder($status): array
    {
        $snap = [
            'orderPrice' => 0,
            'totalCount' => 0,
            'pStatus' => [],
            'snapAddress' => null,
            'snapName' => '',
            'snapImg' => '',
        ];
        $snap['orderPrice'] = $status['orderPrice'];
        $snap['totalCount'] = $status['totalCount'];
        $snap['pStatus'] = $status['pStatusArray'];
        $snap['snapAddress'] = json_encode($this->getUserAddress());
        $snap['snapName'] = $this->products[0]['name'];
        $snap['snapImg'] = $this->products[0]['main_img_url'];
        if(count($this->products)>1){
            $snap['snapName'] .= '等';
        }
        return $snap;
    }
    private function getUserAddress(): array
    {
        $userAddress = UserAddress::where('user_id','=',$this->uid)->find();
        if($userAddress->isEmpty()){
            throw new UserException([
                'msg'=>'用户地址不存在，下单失败',
                'errorCode' => 60001
            ]);
        }
        return $userAddress->toArray();
    }

    /**
     * @param $orderID
     * 支付接口 调用 再次检测库存量 所以是public
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws OrderException
     */
    public function checkOrderStock($orderID): array
    {
        $oProducts = OrderProductModel::where('order_id','=',$orderID)->select();
        $this->oProducts = $oProducts;
        $this->products = $this->getProductsByOrder($oProducts);
        return $status = $this->getOrderStatus();
    }
    /**
     * 库存量检测
     *
     * @throws OrderException
     */
    private function getOrderStatus(): array    {
        $status = [
            'pass' => true,
            'orderPrice' => 0,
            'totalCount'=>0,
            'pStatusArray' => []
        ];
        foreach ($this->oProducts as $oProduct){
            $pStatus = $this->getProductStatus(
                $oProduct['product_id'],$oProduct['count'],$this->products
            );
            if(!$pStatus['haveStock']){
                $status['pass'] = false;
            }
            $status['orderPrice'] += $pStatus['totalPrice'];
            $status['totalCount'] += $pStatus['count'];
            array_push($status['pStatusArray'],$pStatus);
        }
        return $status;
    }

    /**
     * @param $oPID
     * @param $oCount
     * @param $products
     *
     * @return array
     * @throws OrderException
     */
    private function getProductStatus($oPID,$oCount,$products): array
    {
        $pIndex = -1;
        $pStatus = [
            'id' => null,
            'haveStock' => false,
            'count' => 0,
            'name' => '',
            'totalPrice' => 0
        ];
        for($i = 0;$i<count($products);$i++){
            if($oPID == $products[$i]['id']){
                $pIndex = $i;
            }
        }

        if($pIndex == -1){
            // 客户端传递的 product_id 有可能根本不存在
            throw new OrderException([
                'msg' => 'id为'.$oPID.'的商品不存在'
            ]);
        }else{
            $product = $products[$pIndex];
            $pStatus['id'] = $product['id'];
            $pStatus['count'] = $oCount;
            $pStatus['name'] = $product['name'];
            $pStatus['totalPrice'] = $product['price']*$oCount;
            $pStatus['haveStock'] = $product['stock'] >= $oCount;
        }
        return $pStatus;
    }
    /**
     * @param $oProducts
     *
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function getProductsByOrder($oProducts){
        $oPIDs = [];
        foreach ($oProducts as $item){
            array_push($oPIDs,$item['product_id']);
        }

        return Product::select($oPIDs)
                      ->visible(['id','price','stock','name','main_img_url'])
                      ->toArray();
    }
}